Skip to content

feat: implement build pipeline — doc/code generation + deployer (#8, #9, #10)#32

Merged
ComBba merged 2 commits intomainfrom
feat/issue-8-9-10-build-pipeline
Mar 17, 2026
Merged

feat: implement build pipeline — doc/code generation + deployer (#8, #9, #10)#32
ComBba merged 2 commits intomainfrom
feat/issue-8-9-10-build-pipeline

Conversation

@ComBba
Copy link
Contributor

@ComBba ComBba commented Mar 4, 2026

Summary

Key Design Decisions

  • AI-native apps only: Prompts explicitly forbid chatbot wrappers and generic CRUD. Generated apps must embed AI in core business workflows (prediction, recommendation, classification, generation).
  • DO Gradient throughout: Generated backend includes ai_service.py calling DO Serverless Inference API via httpx.
  • Consistent patterns: All nodes follow the established ChatGradient → _parse_json_response → return dict pattern.

Files Changed

  • agent/nodes/doc_generator.py — 141 lines (was 15-line stub)
  • agent/nodes/code_generator.py — 122 lines (was 10-line stub)
  • agent/nodes/deployer.py — 97 lines (was 14-line stub)
  • agent/prompts/doc_templates.py — 95 lines (rich system prompts replacing placeholder Jinja2)
  • agent/prompts/code_templates.py — 50 lines (rich system prompts replacing stubs)
  • web/package.json — Added framer-motion, recharts, react-markdown, react-syntax-highlighter, canvas-confetti
  • web/src/components/ui/* — Added 7 shadcn components (scroll-area, skeleton, progress, alert, avatar, dialog, tooltip)

Verification

  • ruff check passes on all modified Python files
  • python -m py_compile passes on all modified Python files
  • npm run build passes in web/ with new dependencies

Summary by CodeRabbit

릴리즈 노트

  • 새로운 기능

    • 자동 코드 생성(프론트엔드/백엔드) 및 통합
    • GitHub 생성과 DigitalOcean 배포 자동화, 배포 상태/실시간 URL 제공
    • 문서 자동 생성(요구사항, 기술 사양, API 명세, DB 스키마, 앱 스펙)
    • 여러 신규 UI 컴포넌트 추가(Alert, Avatar, Dialog, Progress, ScrollArea, Skeleton, Tooltip)
    • 마크다운 렌더링 및 코드 구문 강조 지원
  • Chores

    • JSON/슬러그/파일경로 유틸리티 추가
    • 시각화·애니메이션 관련 런타임 의존성 추가 (예: confetti, 애니메이션, 차트 등)

@coderabbitai
Copy link

coderabbitai bot commented Mar 4, 2026

Warning

Rate limit exceeded

@ComBba has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 11 minutes and 43 seconds before requesting another review.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 02df19a3-29b6-41fd-a24b-dc31711a41f4

📥 Commits

Reviewing files that changed from the base of the PR and between b2e5747 and 9a242ad.

⛔ Files ignored due to path filters (1)
  • web/package-lock.json is excluded by !**/package-lock.json
📒 Files selected for processing (4)
  • agent/prompts/doc_templates.py
  • agent/utils/__init__.py
  • agent/utils/json_utils.py
  • web/src/components/ui/progress.tsx

Walkthrough

에이전트의 코드 생성, 문서 생성, 배포 노드에 실제 LLM 기반 생성·배포 로직을 구현하고, 문서/코드 프롬프트를 시스템 프롬프트로 재구성하며 프론트엔드 UI 컴포넌트와 JSON 유틸리티를 추가했습니다.

Changes

Cohort / File(s) Summary
Agent: 코드 생성 노드
agent/nodes/code_generator.py
LLM(Claude-4.6) 호출 기반의 전체 파일 생성 흐름 추가: 컨텍스트 구성, _generate_files, _normalize_files_dict, 프론트엔드/백엔드 파일 생성 및 에러 로깅/처리. 반환값에 실제 frontend_code/backend_code 포함.
Agent: 배포 노드
agent/nodes/deployer.py
프론트엔드/백엔드 병합(_merge_files), 리포지토명 생성(_build_repo_name), GitHub 레포 생성 및 DigitalOcean 배포 로직 추가. 상태/URL/app_id를 실제 결과로 채움 및 오류 처리.
Agent: 문서 생성 노드
agent/nodes/doc_generator.py
Anthropic LLM 호출 기반 문서 파이프라인 추가: 컨텍스트 빌드, PRD/Tech/API/DB 스키마 및 app_spec YAML 생성용 헬퍼들과 JSON 파싱 통합.
Prompts: 코드/문서 템플릿
agent/prompts/code_templates.py, agent/prompts/doc_templates.py
여러 템플릿 상수를 시스템 프롬프트형 상수로 재정의·이름 변경(예: CODE_GENERATION_BASE_SYSTEM_PROMPT, DOC_GENERATION_BASE_SYSTEM_PROMPT 등). 기존 플레이스홀더 제거 및 구조화된 지침 추가.
Agent: 유틸리티
agent/utils/json_utils.py
parse_json_response, slugify, is_safe_file_path 추가: LLM 응답 JSON 파싱, 슬러그 생성, 안전한 파일 경로 검사 로직 및 로깅.
Agent: 경량 변경
agent/nodes/vibe_council.py, agent/tools/*
로컬 JSON 파서 사용을 외부 유틸로 대체 및 소소한 포맷/문자열 합침 변경.
Frontend: 의존성 변경
web/package.json
canvas-confetti, framer-motion, react-markdown, react-syntax-highlighter, recharts 및 일부 타입 패키지 추가.
Frontend: UI 컴포넌트
web/src/components/ui/*
alert.tsx, avatar.tsx, dialog.tsx, progress.tsx, scroll-area.tsx, skeleton.tsx, tooltip.tsx
Radix 기반 재사용 UI 컴포넌트들 추가(Alert, Avatar, Dialog, Progress, ScrollArea, Skeleton, Tooltip). 스타일·데이터 슬롯·타입 안전성 포함.

Sequence Diagram(s)

sequenceDiagram
    participant State as Agent State
    participant CodeGen as Code Generator Node
    participant LLM as Claude LLM
    participant JSON as parse_json_response

    State->>CodeGen: idea, generated_docs
    CodeGen->>CodeGen: build context
    CodeGen->>LLM: system prompt + frontend request
    LLM->>JSON: JSON response (files)
    JSON->>CodeGen: frontend_code dict
    CodeGen->>LLM: system prompt + backend request
    LLM->>JSON: JSON response (files)
    JSON->>CodeGen: backend_code dict
    CodeGen->>State: frontend_code, backend_code, phase="code_generated"
Loading
sequenceDiagram
    participant State as Agent State
    participant Deployer as Deployer Node
    participant GitHub as GitHub API
    participant DO as DigitalOcean API

    State->>Deployer: frontend_code, backend_code, idea
    Deployer->>Deployer: _merge_files -> repo files
    Deployer->>GitHub: create repo with files
    GitHub-->>Deployer: repo details / url
    Deployer->>DO: build app spec & deploy
    DO-->>Deployer: app_id, deployment status
    Deployer->>DO: wait for ready (if app_id)
    DO-->>Deployer: live_url when ready
    Deployer->>State: app_id, live_url, github_repo, status, phase="deployed"
Loading
sequenceDiagram
    participant State as Agent State
    participant DocGen as Doc Generator Node
    participant LLM as Anthropic Claude
    participant JSON as parse_json_response

    State->>DocGen: idea, council_analysis, scoring
    DocGen->>DocGen: build planning context
    DocGen->>LLM: request PRD (system + doc prompt)
    LLM->>JSON: response with {"content": "..."}
    JSON->>DocGen: prd content
    DocGen->>LLM: request tech spec / api spec / db schema / app_spec_yaml
    LLM->>JSON: responses
    JSON->>DocGen: generated_docs entries
    DocGen->>State: generated_docs, phase="docs_generated"
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

Poem

🐰 깡총깡총 생각을 모아 말했네,
코드와 문서가 솟아나고 레포가 열렸네.
배포는 구름 타고 멀리 날아가고,
UI 컴포넌트들 반짝이며 춤추네.
당근 한 조각으로 축하하자! 🥕✨

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 8.89% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed 제목이 PR의 핵심 변경사항인 빌드 파이프라인(문서 생성, 코드 생성, 배포자) 구현을 명확하게 요약하고 있습니다.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/issue-8-9-10-build-pipeline
📝 Coding Plan
  • Generate coding plan for human review comments

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@gemini-code-assist
Copy link

Summary of Changes

Hello, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request introduces the complete build pipeline for the VibeDeploy project, encompassing LLM-powered document generation, full-stack code generation, and automated deployment to DigitalOcean. The changes transform stub implementations into robust, AI-native functionalities, ensuring that generated applications are domain-specific and deeply integrate AI into their core logic. The frontend also receives a significant upgrade with new UI components and libraries to support richer content and interactions.

Highlights

  • Doc Generator Implementation: The [M3] Doc Generator: PRD + Tech Spec + API Spec + DB Schema #8 Doc Generator has been fully implemented, leveraging LLMs (claude-4.6-sonnet) via ChatGradient to produce PRD, Tech Spec, API Spec, DB Schema, and DO App Spec YAML documents. Each document is informed by Vibe Council analysis and scoring results.
  • Code Generator Implementation: The [M3] Code Generator: Frontend + Backend code generation #9 Code Generator is now fully functional, generating full-stack application code including a Next.js 15 frontend and a FastAPI backend. It integrates domain-specific AI features via the DigitalOcean Serverless Inference API and returns file maps for both frontend and backend code.
  • Deployer Implementation: The [M3] Deployer: GitHub repo + DO App Platform deployment #10 Deployer has been implemented to automate the deployment process. It creates a GitHub repository using PyGithub, pushes the generated code, builds the DigitalOcean App Platform specification, deploys the application, and polls for the live URL, including graceful partial-failure handling.
  • AI-Native Design Principle: A core design decision enforces that generated applications must be AI-native, explicitly forbidding generic chatbot wrappers or CRUD-only systems. AI features must be embedded in core business workflows like prediction, recommendation, classification, or generation.
  • DigitalOcean Gradient Integration: The entire pipeline consistently integrates DigitalOcean Gradient, with the generated backend's ai_service.py calling the DO Serverless Inference API via httpx, and all nodes following a ChatGradient -> _parse_json_response -> return dict pattern.
  • Frontend UI Enhancements: The web frontend has been significantly enhanced with the addition of several new dependencies, including framer-motion, recharts, react-markdown, react-syntax-highlighter, and canvas-confetti, along with 7 new Shadcn UI components (scroll-area, skeleton, progress, alert, avatar, dialog, tooltip).
Changelog
  • agent/nodes/code_generator.py
    • Implemented LLM-driven frontend and backend code generation.
    • Added utility functions for JSON parsing and file normalization.
    • Integrated ChatGradient for LLM interaction.
  • agent/nodes/deployer.py
    • Implemented logic for creating GitHub repositories and pushing generated code.
    • Integrated DigitalOcean App Platform deployment, including spec building and status polling.
    • Added robust error handling for GitHub and DigitalOcean operations.
    • Introduced helper functions for file merging and repository name generation.
  • agent/nodes/doc_generator.py
    • Implemented LLM-driven generation of PRD, Tech Spec, API Spec, DB Schema, and DigitalOcean App Spec YAML.
    • Integrated ChatGradient for LLM interaction, leveraging product idea and council analysis.
    • Added utility functions for context building and JSON response parsing.
  • agent/prompts/code_templates.py
    • Replaced generic code templates with specific system prompts for frontend (Next.js 15) and backend (FastAPI) generation.
    • Defined constraints for AI-native apps and DigitalOcean Serverless Inference API integration.
  • agent/prompts/doc_templates.py
    • Replaced generic document templates with detailed system prompts for PRD, Tech Spec, API Spec, DB Schema, and DigitalOcean App Spec YAML.
    • Established rules for AI-native, domain-specific app generation and architecture targets.
  • web/package-lock.json
    • Synchronized dependency versions and added new entries for canvas-confetti, framer-motion, react-markdown, react-syntax-highlighter, and recharts.
    • Included various transitive dependencies and their integrity hashes.
  • web/package.json
    • Introduced canvas-confetti, framer-motion, react-markdown, react-syntax-highlighter, and recharts as new dependencies.
    • Added corresponding TypeScript type definitions for new dependencies.
  • web/src/components/ui/alert.tsx
    • Introduced a new Alert component for displaying contextual messages.
    • Provided AlertTitle and AlertDescription for structured content within alerts.
  • web/src/components/ui/avatar.tsx
    • Added Avatar component for user profile pictures, including image, fallback, and badge elements.
    • Implemented AvatarGroup and AvatarGroupCount for displaying collections of avatars.
  • web/src/components/ui/dialog.tsx
    • Introduced a full-featured Dialog component for modal windows.
    • Included sub-components like DialogTrigger, DialogContent, DialogHeader, DialogFooter, DialogTitle, and DialogDescription.
  • web/src/components/ui/progress.tsx
    • Implemented a Progress component to visualize the completion status of tasks.
  • web/src/components/ui/scroll-area.tsx
    • Introduced ScrollArea and ScrollBar components for customizable scrollable regions.
  • web/src/components/ui/skeleton.tsx
    • Implemented a Skeleton component to indicate loading states in the UI.
  • web/src/components/ui/tooltip.tsx
    • Added Tooltip component for displaying informative text on hover or focus.
    • Provided TooltipProvider, TooltipTrigger, and TooltipContent for complete tooltip functionality.
Activity
  • No human activity has been recorded on this pull request yet.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

Copy link

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces a significant feature, implementing the core pipeline for document generation, code generation, and deployment. However, the system is vulnerable to prompt injection due to direct concatenation of untrusted user input into LLM prompts, and it lacks validation for LLM-generated file paths and content. This could lead to Remote Code Execution (RCE) on deployed applications or the GitHub environment. Additionally, the code has instances of duplication, particularly for utility functions like JSON parsing and slug generation, and could benefit from improved error handling and refactoring for better maintainability and robustness. Remediation should prioritize secure prompt engineering practices and strict validation of all LLM-generated outputs.

},
{
"role": "user",
"content": f"Generate frontend files from this product context:\n\n{context}",

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

security-high high

At this line, the code_generator node directly concatenates untrusted user input (from idea and generated_docs state) into the LLM prompt. This creates a prompt injection vulnerability, which could lead to Remote Code Execution (RCE) on deployed applications or malicious GitHub Actions. It is crucial to use clear delimiters (e.g., ### Context ### or XML-style tags like <context>) to separate instructions from untrusted data. Additionally, consider refactoring the functions _generate_frontend_files and _generate_backend_files (lines 43-90) into a single, more generic helper function to reduce duplication and improve maintainability.

Comment on lines +61 to +63
parsed = _parse_json_response(response.content, {"files": {}})
files = parsed.get("files", {})
return _normalize_files_dict(files)

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

security-high high

The code_generator node accepts arbitrary file paths and content from the LLM's output without validation. The _normalize_files_dict function only ensures that keys and values are strings, but does not restrict the paths. These files are then pushed to a GitHub repository in deployer.py. An attacker who successfully performs prompt injection can cause the LLM to generate sensitive files (e.g., .github/workflows/malicious.yml, .env, or overwriting critical application files) which will then be deployed. Implement a strict allow-list for file paths and extensions. Ensure that the LLM cannot generate files in sensitive directories like .github/ or overwrite critical configuration files.

},
{
"role": "user",
"content": f"Create the document from this planning context:\n\n{context}",

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

security-high high

The doc_generator node constructs LLM prompts by directly concatenating untrusted user input (the idea object) into the prompt string without proper sanitization or the use of secure delimiters. This makes the system vulnerable to prompt injection attacks, where a malicious user can provide an "idea" that contains instructions to override the system prompt. Use clear delimiters (e.g., ### Context ### or XML-style tags like <context>) to separate instructions from untrusted data.

Comment on lines +72 to +86
def _merge_files(frontend_code: dict, backend_code: dict) -> dict[str, str]:
merged: dict[str, str] = {}

for path, content in backend_code.items():
if isinstance(path, str) and isinstance(content, str):
merged[path] = content

for path, content in frontend_code.items():
if not isinstance(path, str) or not isinstance(content, str):
continue

normalized_path = path if path.startswith("web/") else f"web/{path}"
merged[normalized_path] = content

return merged

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

security-high high

The _merge_files function merges frontend and backend code into a single dictionary of files to be pushed to GitHub. However, it does not validate the file paths provided in the backend_code dictionary. If the LLM is manipulated via prompt injection to generate malicious file paths (e.g., .github/workflows/attack.yml), this function will include them in the final file set, leading to potential RCE on the GitHub runner or deployment of backdoored code. Implement path validation to ensure that only allowed directories and file types are included.

Comment on lines +116 to +120
def _slugify(value: str) -> str:
clean = re.sub(r"[^a-zA-Z0-9\s-]", "", value).strip().lower()
clean = re.sub(r"[\s_]+", "-", clean)
clean = re.sub(r"-+", "-", clean)
return clean or "vibedeploy-app"

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

This _slugify function is very similar to the slug generation logic in _build_repo_name in agent/nodes/deployer.py. However, it's missing the logic to truncate the slug to 45 characters and strip trailing hyphens. This could lead to inconsistencies where the generated repo placeholder URL in the documentation does not match the actual repository name created by the deployer. These two functions should be consolidated into a single, shared utility to ensure consistency.

References
  1. Avoid code duplication (DRY principle). When two or more pieces of code are very similar, they should be refactored into a single reusable component or function to improve maintainability and ensure consistency.

Comment on lines +117 to +118
except json.JSONDecodeError:
pass

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

Silently passing on a json.JSONDecodeError can hide issues with the LLM's output and make debugging difficult. If the regex match is not valid JSON, this error will be swallowed, and the function will return a default value, potentially masking an underlying problem. It is better to log this exception to aid in debugging.

References
  1. Error handling should not silently ignore exceptions, as this can hide bugs and make debugging difficult. At a minimum, exceptions should be logged to provide visibility into potential issues.

Comment on lines +11 to +13
frontend_code = state.get("frontend_code", {}) or {}
backend_code = state.get("backend_code", {}) or {}
idea = state.get("idea", {}) or {}

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The or {} is redundant in expressions like state.get("key", {}) or {}. The state.get("key", {}) part already provides an empty dictionary if the key is missing. If the key's value could be None, a more idiomatic way to handle this is state.get("key") or {}. This change improves code clarity and conciseness.

Suggested change
frontend_code = state.get("frontend_code", {}) or {}
backend_code = state.get("backend_code", {}) or {}
idea = state.get("idea", {}) or {}
frontend_code = state.get("frontend_code") or {}
backend_code = state.get("backend_code") or {}
idea = state.get("idea") or {}

Comment on lines +123 to +141
def _parse_json_response(content: str, default: dict) -> dict:
content = content.strip()
if content.startswith("```"):
content = re.sub(r"^```(?:json)?\n?", "", content)
content = re.sub(r"\n?```$", "", content)

try:
return json.loads(content)
except json.JSONDecodeError:
json_match = re.search(r"\{[\s\S]*\}", content)
if json_match:
try:
return json.loads(json_match.group())
except json.JSONDecodeError:
pass

result = dict(default)
result["raw_response"] = content[:500]
return result

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The function _parse_json_response is identical to the one in agent/nodes/code_generator.py. This code duplication violates the DRY (Don't Repeat Yourself) principle and makes maintenance harder, as any changes would need to be applied in multiple places. Consider creating a shared utils.py module within the agent package to house common utility functions like this one.

References
  1. Avoid code duplication (DRY principle). When two or more pieces of code are very similar, they should be refactored into a single reusable component or function to improve maintainability and reduce the chance of introducing bugs when making changes.

Copy link

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: a1ddb2bc71

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".



def _slugify(value: str) -> str:
clean = re.sub(r"[^a-zA-Z0-9\s-]", "", value).strip().lower()

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Coerce idea name before slugifying app spec name

_slugify applies re.sub directly to value, but _generate_app_spec_yaml_doc passes idea.get("name")/idea.get("tagline") without type coercion; if the LLM returns a non-string field (for example a list/object), this raises TypeError and the docs phase fails entirely. Guarding/casting to string here (like _build_repo_name does) prevents one malformed idea field from crashing document generation.

Useful? React with 👍 / 👎.

"phase": "deployed",
}

app_spec = build_app_spec(app_name, github_clone_url)

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Pass the actual repo branch into deployment spec

build_app_spec is called without a branch argument, so it always deploys branch main; this breaks in orgs/users whose default branch is not main, where App Platform will track a nonexistent branch and deployment fails despite successful repo creation. Capture the created repository’s default branch and pass it through so deployment follows the branch that actually exists.

Useful? React with 👍 / 👎.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🧹 Nitpick comments (5)
agent/nodes/doc_generator.py (2)

116-120: slug 생성 로직 중복

_slugifydeployer.py_build_repo_name과 유사한 정규화 로직을 사용합니다. 공통 유틸리티로 추출하면 일관성을 유지할 수 있습니다.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@agent/nodes/doc_generator.py` around lines 116 - 120, The slug normalization
logic in _slugify duplicates the regex-based normalization in deployer.py's
_build_repo_name; extract a shared utility (e.g., normalize_name/normalize_slug)
into a common module (utility or utils) and replace both _slugify in
agent/nodes/doc_generator.py and _build_repo_name in deployer.py to call that
shared function; ensure the new utility preserves current behavior (remove
non-alphanumerics, collapse whitespace/underscores to single hyphens, collapse
repeated hyphens, lowercase, and fallback to "vibedeploy-app") and update
imports in the two modules.

92-97: 프롬프트 지시문 불일치

APP_SPEC_SYSTEM_PROMPT는 "Return YAML only. No markdown fences."라고 지시하지만, Line 96에서 "Return JSON with one key: 'content' containing only YAML."로 덮어씁니다. LLM에 혼란을 줄 수 있으므로, doc_templates.pyAPP_SPEC_SYSTEM_PROMPT에서 원본 반환 지시문을 제거하거나 JSON 래퍼 패턴을 명시하는 것을 권장합니다.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@agent/nodes/doc_generator.py` around lines 92 - 97, The system prompts are
inconsistent: DOC_GENERATION_BASE_SYSTEM_PROMPT + APP_SPEC_SYSTEM_PROMPT (from
doc_templates.py) currently instruct YAML-only, but doc_generator.py builds a
system message that says "Return JSON with one key: 'content' containing only
YAML."; fix by making the instructions unambiguous — either remove the "Return
YAML only. No markdown fences." sentence from APP_SPEC_SYSTEM_PROMPT in
doc_templates.py, or update APP_SPEC_SYSTEM_PROMPT to explicitly state the JSON
wrapper pattern (e.g., "Return JSON with one key: 'content' whose value is YAML,
no markdown fences"), and ensure the assembled system message in
doc_generator.py (where the f-string combines DOC_GENERATION_BASE_SYSTEM_PROMPT
and APP_SPEC_SYSTEM_PROMPT) reflects that single, consistent instruction.
agent/nodes/deployer.py (1)

94-97: 타임스탬프 접미사 충돌 가능성

time.time()의 마지막 6자리를 사용하면 약 11.5일 주기로 값이 반복되어 동일한 slug 기반에서 충돌 가능성이 있습니다. MVP에서는 허용 가능하나, 프로덕션에서는 UUID 일부 사용을 고려해보세요.

♻️ 더 고유한 접미사 사용 제안
+import uuid
+
 def _build_repo_name(idea: dict) -> str:
     # ... slug generation ...
-    suffix = str(int(time.time()))[-6:]
+    suffix = uuid.uuid4().hex[:8]
     return f"{slug}-{suffix}"
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@agent/nodes/deployer.py` around lines 94 - 97, The timestamp-based suffix
generation for slug in deployer.py (the slug variable and the current suffix =
str(int(time.time()))[-6:]) can collide every ~11.5 days; replace it with a more
unique suffix by combining the timestamp with a truncated UUID (e.g., use
int(time.time()) or its last digits plus uuid.uuid4().hex[:6] or similar) so the
return value f"{slug}-{suffix}" becomes much less likely to collide; update the
code that computes suffix to use uuid.uuid4() (or uuid.uuid4().hex slice)
alongside or instead of the time-based portion and keep the same final return
format.
agent/nodes/code_generator.py (2)

43-90: 중복 코드 리팩토링 권장

_generate_frontend_files_generate_backend_files는 프롬프트와 메시지만 다르고 거의 동일한 구조입니다. 공통 헬퍼로 추출하면 유지보수성이 향상됩니다.

♻️ 공통 헬퍼 추출 제안
async def _generate_files(
    llm: ChatGradient,
    system_prompt: str,
    user_message: str,
    context: str
) -> dict[str, str]:
    response = await llm.ainvoke([
        {
            "role": "system",
            "content": (
                f"{CODE_GENERATION_BASE_SYSTEM_PROMPT}\n\n"
                f"{system_prompt}\n\n"
                "Return JSON object with exactly one top-level key: 'files'."
            ),
        },
        {"role": "user", "content": f"{user_message}\n\n{context}"},
    ])
    parsed = _parse_json_response(response.content, {"files": {}})
    return _normalize_files_dict(parsed.get("files", {}))
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@agent/nodes/code_generator.py` around lines 43 - 90, Both
_generate_frontend_files and _generate_backend_files duplicate the same LLM
invocation/response parsing logic; extract a shared helper (e.g.,
_generate_files) that accepts the ChatGradient llm, the system prompt fragment
(FRONTEND_SYSTEM_PROMPT or BACKEND_SYSTEM_PROMPT), and the user message prefix,
then performs the ainvoke call, calls _parse_json_response(..., {"files": {}})
and returns _normalize_files_dict(parsed.get("files", {})). Replace
_generate_frontend_files and _generate_backend_files to call this new
_generate_files helper with the appropriate system_prompt and user message to
preserve behavior and responses.

33-34: LLM 호출 실패 시 예외 처리 부재

_generate_frontend_files_generate_backend_files 호출 시 LLM 오류가 발생하면 예외가 전파되어 전체 파이프라인이 중단됩니다. deployer.py처럼 부분 실패를 허용하는 구조를 고려해보세요.

♻️ 예외 처리 추가 제안
+    try:
+        frontend_code = await _generate_frontend_files(llm, context)
+    except Exception as e:
+        frontend_code = {"error": f"Frontend generation failed: {str(e)[:200]}"}
+
+    try:
+        backend_code = await _generate_backend_files(llm, context)
+    except Exception as e:
+        backend_code = {"error": f"Backend generation failed: {str(e)[:200]}"}
-    frontend_code = await _generate_frontend_files(llm, context)
-    backend_code = await _generate_backend_files(llm, context)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@agent/nodes/code_generator.py` around lines 33 - 34, LLM 호출 실패 시 예외가 전파되어
파이프라인이 중단되므로 `_generate_frontend_files`와 `_generate_backend_files` 호출을 각각
try/except로 감싸 예외를 잡고 처리하세요: 각 호출에서 Exception을 잡아(예: `except Exception as e`)
오류를 로그하고(`logger.error` 또는 프로젝트의 로거 사용), 실패 시 해당 결과 변수(`frontend_code`,
`backend_code`)를 안전한 기본값(예: None 또는 빈 구조)으로 설정하여 나머지 파이프라인이 계속 실행되도록 변경하세요; 동작
방식은 이미 부분 실패를 허용하는 `deployer.py`의 처리 패턴을 참고해 일관되게 적용하세요.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@agent/nodes/code_generator.py`:
- Around line 104-122: Duplicate implementation of _parse_json_response across
code_generator.py, doc_generator.py, and vibe_council.py should be refactored
into a single utility: create a new function (keep name _parse_json_response or
rename to parse_json_response) in a shared module agent/utils/json_utils.py
implementing the exact same logic (strip, remove triple-backticks, attempt
json.loads, fallback regex extract, and attach raw_response on failure), then
replace the three in-file definitions with an import from agent.utils.json_utils
and update callers in code_generator.py, doc_generator.py, and vibe_council.py
to use the centralized function so behavior and signature remain identical.

In `@web/src/components/ui/progress.tsx`:
- Line 25: The progress indicator uses value directly in the transform (style={{
transform: `translateX(-${100 - (value || 0)}%)` }}) which can push the
indicator outside its container for values <0 or >100; clamp the incoming value
in the Progress component (or where prop 'value' is handled) to the 0–100 range
(e.g., via Math.max(0, Math.min(100, value || 0)) or a small clamp utility) and
use that clampedValue in the transform calculation so the indicator never
escapes the container.

---

Nitpick comments:
In `@agent/nodes/code_generator.py`:
- Around line 43-90: Both _generate_frontend_files and _generate_backend_files
duplicate the same LLM invocation/response parsing logic; extract a shared
helper (e.g., _generate_files) that accepts the ChatGradient llm, the system
prompt fragment (FRONTEND_SYSTEM_PROMPT or BACKEND_SYSTEM_PROMPT), and the user
message prefix, then performs the ainvoke call, calls _parse_json_response(...,
{"files": {}}) and returns _normalize_files_dict(parsed.get("files", {})).
Replace _generate_frontend_files and _generate_backend_files to call this new
_generate_files helper with the appropriate system_prompt and user message to
preserve behavior and responses.
- Around line 33-34: LLM 호출 실패 시 예외가 전파되어 파이프라인이 중단되므로
`_generate_frontend_files`와 `_generate_backend_files` 호출을 각각 try/except로 감싸 예외를
잡고 처리하세요: 각 호출에서 Exception을 잡아(예: `except Exception as e`) 오류를
로그하고(`logger.error` 또는 프로젝트의 로거 사용), 실패 시 해당 결과 변수(`frontend_code`,
`backend_code`)를 안전한 기본값(예: None 또는 빈 구조)으로 설정하여 나머지 파이프라인이 계속 실행되도록 변경하세요; 동작
방식은 이미 부분 실패를 허용하는 `deployer.py`의 처리 패턴을 참고해 일관되게 적용하세요.

In `@agent/nodes/deployer.py`:
- Around line 94-97: The timestamp-based suffix generation for slug in
deployer.py (the slug variable and the current suffix =
str(int(time.time()))[-6:]) can collide every ~11.5 days; replace it with a more
unique suffix by combining the timestamp with a truncated UUID (e.g., use
int(time.time()) or its last digits plus uuid.uuid4().hex[:6] or similar) so the
return value f"{slug}-{suffix}" becomes much less likely to collide; update the
code that computes suffix to use uuid.uuid4() (or uuid.uuid4().hex slice)
alongside or instead of the time-based portion and keep the same final return
format.

In `@agent/nodes/doc_generator.py`:
- Around line 116-120: The slug normalization logic in _slugify duplicates the
regex-based normalization in deployer.py's _build_repo_name; extract a shared
utility (e.g., normalize_name/normalize_slug) into a common module (utility or
utils) and replace both _slugify in agent/nodes/doc_generator.py and
_build_repo_name in deployer.py to call that shared function; ensure the new
utility preserves current behavior (remove non-alphanumerics, collapse
whitespace/underscores to single hyphens, collapse repeated hyphens, lowercase,
and fallback to "vibedeploy-app") and update imports in the two modules.
- Around line 92-97: The system prompts are inconsistent:
DOC_GENERATION_BASE_SYSTEM_PROMPT + APP_SPEC_SYSTEM_PROMPT (from
doc_templates.py) currently instruct YAML-only, but doc_generator.py builds a
system message that says "Return JSON with one key: 'content' containing only
YAML."; fix by making the instructions unambiguous — either remove the "Return
YAML only. No markdown fences." sentence from APP_SPEC_SYSTEM_PROMPT in
doc_templates.py, or update APP_SPEC_SYSTEM_PROMPT to explicitly state the JSON
wrapper pattern (e.g., "Return JSON with one key: 'content' whose value is YAML,
no markdown fences"), and ensure the assembled system message in
doc_generator.py (where the f-string combines DOC_GENERATION_BASE_SYSTEM_PROMPT
and APP_SPEC_SYSTEM_PROMPT) reflects that single, consistent instruction.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: ff5ff231-9c91-424e-b4ae-e4cc8e99f4c1

📥 Commits

Reviewing files that changed from the base of the PR and between a854673 and a1ddb2b.

⛔ Files ignored due to path filters (1)
  • web/package-lock.json is excluded by !**/package-lock.json
📒 Files selected for processing (13)
  • agent/nodes/code_generator.py
  • agent/nodes/deployer.py
  • agent/nodes/doc_generator.py
  • agent/prompts/code_templates.py
  • agent/prompts/doc_templates.py
  • web/package.json
  • web/src/components/ui/alert.tsx
  • web/src/components/ui/avatar.tsx
  • web/src/components/ui/dialog.tsx
  • web/src/components/ui/progress.tsx
  • web/src/components/ui/scroll-area.tsx
  • web/src/components/ui/skeleton.tsx
  • web/src/components/ui/tooltip.tsx

@chatgpt-codex-connector
Copy link

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.

1 similar comment
@chatgpt-codex-connector
Copy link

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 5

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@agent/nodes/deployer.py`:
- Around line 14-28: The deploy path currently proceeds even if one side's code
is missing; before calling create_github_repo or any deploy steps, add a guard
in agent/nodes/deployer.py that validates frontend_code and backend_code are
non-empty dicts (the outputs from code_generator()), and if either is empty,
stop the flow (raise/return an error or log and abort) so you don't create a
broken repo or call build_app_spec() from agent/tools/digitalocean.py; reference
the existing variables frontend_code, backend_code, app_name and the call to
create_github_repo to locate where to insert this check and ensure the guard
triggers before any external resource creation.
- Around line 33-71: The code currently returns phase "deployed" even on
GitHub/DigitalOcean errors and when wait_for_deployment returns empty (which can
indicate ERROR/CANCELED); update the error branches and post-deploy logic so
hard failures surface as "deployment_error" while only true pending flows use
"deployment_started": in the early GitHub error branch (github_result /
github_clone_url) and the deploy_result.get("status") == "error" branch, change
the returned "phase" from "deployed" to "deployment_error"; after calling
wait_for_deployment(app_id) treat an empty live_url as "deployment_started" only
when there is a valid app_id and deploy_result.get("status") is not "error"
(otherwise set phase to "deployment_error"); reference deploy_to_digitalocean,
wait_for_deployment, github_result, and deploy_result to locate the changes.

In `@agent/nodes/doc_generator.py`:
- Around line 23-35: The sequence of five LLM calls (using llm and context)
calls _generate_markdown_doc and _generate_app_spec_yaml_doc without any
isolation so one failure aborts the whole node; wrap each call to
_generate_markdown_doc (for PRD_SYSTEM_PROMPT, TECH_SPEC_SYSTEM_PROMPT,
API_SPEC_SYSTEM_PROMPT, DB_SCHEMA_SYSTEM_PROMPT) and _generate_app_spec_yaml_doc
in its own try/except, capture exceptions and store per-document results (e.g.,
{name: {success: bool, content: str|null, error: str|null}}), and return that
structured result from the current function instead of letting a single
exception propagate; ensure you reference the llm/context variables and keep
original prompts (PRD_SYSTEM_PROMPT, TECH_SPEC_SYSTEM_PROMPT,
API_SPEC_SYSTEM_PROMPT, DB_SCHEMA_SYSTEM_PROMPT) when invoking each call so
callers can handle partial failures.

In `@agent/utils/json_utils.py`:
- Around line 75-88: The is_safe_file_path function currently only strips
leading slashes and checks startswith, which allows traversal and nested
sensitive names to bypass (e.g., web/../.github, config/.env); fix by fully
normalizing and sanitizing the path first (use os.path.normpath or
pathlib.Path.resolve-like normalization without following symlinks), reject any
path containing ".." segments or leading "..", split the normalized path into
components and ensure none of the components exactly match entries in
_BLOCKED_EXACT_FILES and none of the path prefixes match _BLOCKED_PATH_PREFIXES
(also treat names like ".env.local" as sensitive by matching component
startswith ".env"), and keep the final boolean behavior in is_safe_file_path.
- Around line 35-54: The current JSON parsing returns any JSON type
(list/string/etc.), which breaks callers like agent/nodes/code_generator.py and
doc_generator.py that expect a dict; after each successful json.loads (both the
initial parse and the fallback json_match.parse), verify the result is a dict
(mapping) and if not, fall back to creating result = dict(default) with
result["raw_response"] = content[:500] and return that; preserve existing
warning logs where parsing fails but ensure non-dict parsed values are treated
the same as parse failures so callers always receive a dict.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 04eb350d-dd12-493a-bff9-b1e0e31d6e2a

📥 Commits

Reviewing files that changed from the base of the PR and between a1ddb2b and b2e5747.

📒 Files selected for processing (10)
  • agent/nodes/code_generator.py
  • agent/nodes/deployer.py
  • agent/nodes/doc_generator.py
  • agent/nodes/vibe_council.py
  • agent/prompts/doc_templates.py
  • agent/tools/knowledge_base.py
  • agent/tools/web_search.py
  • agent/utils/__init__.py
  • agent/utils/json_utils.py
  • web/src/components/ui/progress.tsx
✅ Files skipped from review due to trivial changes (1)
  • agent/tools/web_search.py

Comment on lines +33 to 71
if github_result.get("status") == "error" or not github_clone_url:
error_message = github_result.get("error", "GitHub repository creation failed")
return {
"deploy_result": {
"app_id": "",
"live_url": "",
"github_repo": github_repo_url,
"status": f"github_error: {error_message}",
},
"phase": "deployed",
}

app_spec = build_app_spec(app_name, github_clone_url)
deploy_result = await deploy_to_digitalocean(github_clone_url, app_spec)

if deploy_result.get("status") == "error":
error_message = deploy_result.get("error", "DigitalOcean deployment failed")
return {
"deploy_result": {
"app_id": deploy_result.get("app_id", ""),
"live_url": "",
"github_repo": github_repo_url,
"status": f"deployment_error: {error_message}",
},
"phase": "deployed",
}

app_id = deploy_result.get("app_id", "")
live_url = await wait_for_deployment(app_id) if app_id else ""

status = "deployed" if live_url else "deployment_started"
return {
"deploy_result": {
"app_id": "stub-app-id",
"live_url": "https://example.ondigitalocean.app",
"github_repo": "https://github.com/example/vibedeploy-stub",
"status": "deployed",
"app_id": app_id,
"live_url": live_url,
"github_repo": github_repo_url,
"status": status,
},
"phase": "deployed",
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

배포 실패가 성공 단계로 보고됩니다.

GitHub/DO 오류 분기에서도 phase가 계속 "deployed"이고, agent/tools/digitalocean.pywait_for_deployment()ERROR/CANCELED에서도 빈 문자열을 반환합니다. 지금 구조면 실제 실패가 대시보드에서 성공 또는 진행 중으로 보일 수 있습니다. hard failure는 deployment_error, 실제 대기 상태만 deployment_started로 분리해서 올려야 합니다.

As per coding guidelines, "Preserve event-friendly phase names because SSE/dashboard code depends on them".

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@agent/nodes/deployer.py` around lines 33 - 71, The code currently returns
phase "deployed" even on GitHub/DigitalOcean errors and when wait_for_deployment
returns empty (which can indicate ERROR/CANCELED); update the error branches and
post-deploy logic so hard failures surface as "deployment_error" while only true
pending flows use "deployment_started": in the early GitHub error branch
(github_result / github_clone_url) and the deploy_result.get("status") ==
"error" branch, change the returned "phase" from "deployed" to
"deployment_error"; after calling wait_for_deployment(app_id) treat an empty
live_url as "deployment_started" only when there is a valid app_id and
deploy_result.get("status") is not "error" (otherwise set phase to
"deployment_error"); reference deploy_to_digitalocean, wait_for_deployment,
github_result, and deploy_result to locate the changes.

Comment on lines +23 to +35
llm = ChatGradient(
model="anthropic-claude-4.6-sonnet",
temperature=0.3,
max_tokens=4000,
)

context = _build_context(idea, council_analysis, scoring)

prd = await _generate_markdown_doc(llm, PRD_SYSTEM_PROMPT, context)
tech_spec = await _generate_markdown_doc(llm, TECH_SPEC_SYSTEM_PROMPT, context)
api_spec = await _generate_markdown_doc(llm, API_SPEC_SYSTEM_PROMPT, context)
db_schema = await _generate_markdown_doc(llm, DB_SCHEMA_SYSTEM_PROMPT, context)
app_spec_yaml = await _generate_app_spec_yaml_doc(llm, context, idea)
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

문서 생성 호출 하나만 실패해도 노드 전체가 중단됩니다.

여기는 LLM 호출을 다섯 번 순차 실행하지만 예외를 전혀 격리하지 않습니다. provider/network 오류 하나만 나도 워크플로가 예외로 종료되고 이후 단계가 전부 막힙니다. 문서별 실패를 개별적으로 처리하거나 최소한 구조화된 오류 상태를 반환해야 합니다.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@agent/nodes/doc_generator.py` around lines 23 - 35, The sequence of five LLM
calls (using llm and context) calls _generate_markdown_doc and
_generate_app_spec_yaml_doc without any isolation so one failure aborts the
whole node; wrap each call to _generate_markdown_doc (for PRD_SYSTEM_PROMPT,
TECH_SPEC_SYSTEM_PROMPT, API_SPEC_SYSTEM_PROMPT, DB_SCHEMA_SYSTEM_PROMPT) and
_generate_app_spec_yaml_doc in its own try/except, capture exceptions and store
per-document results (e.g., {name: {success: bool, content: str|null, error:
str|null}}), and return that structured result from the current function instead
of letting a single exception propagate; ensure you reference the llm/context
variables and keep original prompts (PRD_SYSTEM_PROMPT, TECH_SPEC_SYSTEM_PROMPT,
API_SPEC_SYSTEM_PROMPT, DB_SCHEMA_SYSTEM_PROMPT) when invoking each call so
callers can handle partial failures.

Comment on lines +35 to +54
try:
return json.loads(content)
except json.JSONDecodeError:
json_match = re.search(r"\{[\s\S]*\}", content)
if json_match:
try:
return json.loads(json_match.group())
except json.JSONDecodeError:
logger.warning(
"Failed to parse extracted JSON block (length=%d)",
len(json_match.group()),
)

logger.warning(
"Returning default for unparseable LLM response (length=%d)",
len(content),
)
result = dict(default)
result["raw_response"] = content[:500]
return result
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

최상위 JSON이 객체가 아닐 때 호출부가 바로 깨집니다.

json.loads()는 리스트나 문자열도 정상 파싱으로 반환하는데, 여기서는 타입 검증 없이 그대로 돌려줍니다. 이 유틸을 쓰는 agent/nodes/code_generator.pyagent/nodes/doc_generator.py는 곧바로 .get()을 호출하므로, 모델이 []"..." 같은 유효한 JSON을 내보내면 AttributeError가 납니다. 객체가 아닐 때는 default로 폴백해야 합니다.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@agent/utils/json_utils.py` around lines 35 - 54, The current JSON parsing
returns any JSON type (list/string/etc.), which breaks callers like
agent/nodes/code_generator.py and doc_generator.py that expect a dict; after
each successful json.loads (both the initial parse and the fallback
json_match.parse), verify the result is a dict (mapping) and if not, fall back
to creating result = dict(default) with result["raw_response"] = content[:500]
and return that; preserve existing warning logs where parsing fails but ensure
non-dict parsed values are treated the same as parse failures so callers always
receive a dict.

Comment on lines +75 to +88
def is_safe_file_path(path: str) -> bool:
"""Return ``True`` when *path* is safe to write to a generated repo.

Blocks sensitive directories (``.github/``, ``.git/``) and files
(``.env``, ``Dockerfile``) that could be exploited via prompt
injection.
"""
normalized = path.lstrip("/")
if normalized in _BLOCKED_EXACT_FILES:
return False
for prefix in _BLOCKED_PATH_PREFIXES:
if normalized.startswith(prefix):
return False
return True
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

민감 경로 차단이 ..와 중첩 .env를 우회당합니다.

lstrip("/")startswith()만 검사하면 web/../.github/workflows/deploy.yml, src/../../.env, config/.env, web/.env.local 같은 경로가 그대로 통과합니다. 프롬프트 인젝션 방어가 목적이라면 경로를 먼저 정규화하고, ./.. 세그먼트와 모든 민감한 path component를 차단해야 합니다.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@agent/utils/json_utils.py` around lines 75 - 88, The is_safe_file_path
function currently only strips leading slashes and checks startswith, which
allows traversal and nested sensitive names to bypass (e.g., web/../.github,
config/.env); fix by fully normalizing and sanitizing the path first (use
os.path.normpath or pathlib.Path.resolve-like normalization without following
symlinks), reject any path containing ".." segments or leading "..", split the
normalized path into components and ensure none of the components exactly match
entries in _BLOCKED_EXACT_FILES and none of the path prefixes match
_BLOCKED_PATH_PREFIXES (also treat names like ".env.local" as sensitive by
matching component startswith ".env"), and keep the final boolean behavior in
is_safe_file_path.

ComBba added 2 commits March 17, 2026 14:32
…g, robustness

- Extract shared parse_json_response, slugify, is_safe_file_path into agent/utils/json_utils.py (DRY across 3 files)
- Add file path allow-list to block .github/, .env, .git/ in generated code (security)
- Add LLM call exception handling in code_generator to prevent pipeline crash (robustness)
- Deduplicate _generate_frontend/backend_files into single _generate_files helper
- Replace timestamp suffix with uuid4 in deployer repo naming (collision resistance)
- Fix redundant 'or {}' pattern in deployer.py state access
- Add logging for JSON parse failures instead of silent pass
- Fix APP_SPEC_SYSTEM_PROMPT inconsistency (YAML-only vs JSON wrapper)
- Clamp Progress component value to 0-100 range (UI safety)
- Add type coercion in slugify for non-string inputs
@ComBba ComBba force-pushed the feat/issue-8-9-10-build-pipeline branch from b2e5747 to 9a242ad Compare March 17, 2026 05:35
@chatgpt-codex-connector
Copy link

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.

@ComBba ComBba merged commit d114b24 into main Mar 17, 2026
3 of 4 checks passed
@ComBba ComBba deleted the feat/issue-8-9-10-build-pipeline branch March 18, 2026 01:42
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant